/**

 @Name：layui.form 表单组件
 @Author：贤心
 @License：MIT

 */

layui.define('layer', function (exports) {
  'use strict'

  var $ = layui.$
  var layer = layui.layer
  var hint = layui.hint()
  var device = layui.device()

  var MOD_NAME = 'form'; var ELEM = '.layui-form'; var THIS = 'layui-this'; var SHOW = 'layui-show'; var HIDE = 'layui-hide'; var DISABLED = 'layui-disabled'

  var Form = function () {
    this.config = {
      verify: {
        required: [
          /[\S]+/,
          '必填项不能为空'
        ],
        phone: [
          /^1\d{10}$/,
          '请输入正确的手机号'
        ],
        email: [
          /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
          '邮箱格式不正确'
        ],
        url: [
          /(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,
          '链接格式不正确'
        ],
        number: function (value) {
          if (!value || isNaN(value)) return '只能填写数字'
        },
        date: [
          /^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,
          '日期格式不正确'
        ],
        identity: [
          /(^\d{15}$)|(^\d{17}(x|X|\d)$)/,
          '请输入正确的身份证号'
        ]
      }
    }
  }

  // 全局设置
  Form.prototype.set = function (options) {
    var that = this
    $.extend(true, that.config, options)
    return that
  }

  // 验证规则设定
  Form.prototype.verify = function (settings) {
    var that = this
    $.extend(true, that.config.verify, settings)
    return that
  }

  // 表单事件监听
  Form.prototype.on = function (events, callback) {
    return layui.onevent.call(this, MOD_NAME, events, callback)
  }

  // 表单控件渲染
  Form.prototype.render = function (type, filter) {
    var that = this
    that.id = 0
    that.getId = function () {
		  return ++that.id
    }
    var elemForm = $(ELEM + (function () {
      return filter ? ('[lay-filter="' + filter + '"]') : ''
    }()))
    var items = {

      // 下拉选择框
    	/**/
      select: function () {
        var TIPS = '请选择'; var CLASS = 'layui-form-select'; var TITLE = 'layui-select-title'
        var NONE = 'layui-select-none'; var initValue = ''; var thatInput

        var selects = elemForm.find('select'); var hide = function (e, clear) {
        	var reElem = $(e.target).parent()

          if (!reElem.hasClass(TITLE) || clear) {
            $('.' + CLASS).removeClass(CLASS + 'ed ' + CLASS + 'up')
            $('.lay-select-dropdown').removeClass('lay-select-dropdown-select').addClass('lay-select-dropdown-unselect')
            thatInput && initValue && thatInput.val(initValue)
          }
          thatInput = null
        }

        var events = function (reElem, disabled, isSearch) {
          var select = $(this)
          var title = reElem.find('.' + TITLE)
          var input = title.find('input')
          var hiddenInput = reElem.find('.select-input-hidden')
          var dropMenuId = $(reElem).attr('dropmenu')
          var dropMenu = $('#' + dropMenuId)
          var dl = dropMenu.find('dl')
          var dds = dl.children('dd')

          if (disabled) return

          // 展开下拉
          var showDown = function () {
            var top = reElem.offset().top + reElem.outerHeight() + 5 - win.scrollTop()
            var left = reElem.offset().left
            var width = reElem.width()
            var dlHeight = dl.outerHeight()
            reElem.addClass(CLASS + 'ed')
            dds.removeClass(HIDE)

            $(dropMenu).css('width', width)
            $(dropMenu).css('top', top)
            $(dropMenu).css('left', left)
            $(dropMenu).css('zIndex', 99999999)
            $(dropMenu).removeClass('lay-select-dropdown-unselect').addClass('lay-select-dropdown-select')
            // 上下定位识别
            if (top + dlHeight > win.height() && top >= dlHeight) {
              reElem.addClass(CLASS + 'up')
            }
          }; var hideDown = function (choose) {
            reElem.removeClass(CLASS + 'ed ' + CLASS + 'up')
            input.blur()

            var dropMenuId = $(reElem).attr('dropmenu')
            var dropMenu = $('#' + dropMenuId)
            $(dropMenu).removeClass('lay-select-dropdown-select').addClass('lay-select-dropdown-unselect')
            if (choose) return
            notOption(input.val(), function (none) {
              if (none) {
                initValue = dl.find('.' + THIS).html()
                input && input.val(initValue)
              }
            })
          }

          // 点击标题区域
          title.on('click', function (e) {
            reElem.hasClass(CLASS + 'ed') ? (
              hideDown()
            ) : (
              hide(e, true),
              showDown()
            )
            dl.find('.' + NONE).remove()
          })

          // 点击箭头获取焦点
          title.find('.layui-edge').on('click', function () {
            input.focus()
          })

          // 键盘事件
          input.on('keyup', function (e) {
            var keyCode = e.keyCode
            // Tab键
            if (keyCode === 9) {
              showDown()
            }
          }).on('keydown', function (e) {
            var keyCode = e.keyCode
            // Tab键
            if (keyCode === 9) {
              hideDown()
            } else if (keyCode === 13) { // 回车键
              e.preventDefault()
            }
          })

          // 检测值是否不属于select项
          var notOption = function (value, callback, origin) {
            var num = 0
            layui.each(dds, function () {
              var othis = $(this)
              var text = othis.text()
              var not = text.indexOf(value) === -1
              if (value === '' || (origin === 'blur') ? value !== text : not) num++
              origin === 'keyup' && othis[not ? 'addClass' : 'removeClass'](HIDE)
            })
            var none = num === dds.length
            return callback(none), none
          }

          // 搜索匹配
          var search = function (e) {
            var value = this.value; var keyCode = e.keyCode

            if (keyCode === 9 || keyCode === 13 ||
              keyCode === 37 || keyCode === 38 ||
              keyCode === 39 || keyCode === 40
            ) {
              return false
            }

            notOption(value, function (none) {
              if (none) {
                dl.find('.' + NONE)[0] || dl.append('<p class="' + NONE + '">无匹配项</p>')
              } else {
                dl.find('.' + NONE).remove()
              }
            }, 'keyup')

            if (value === '') {
              dl.find('.' + NONE).remove()
            }
          }

          if (isSearch) {
            input.on('keyup', search).on('blur', function (e) {
              thatInput = input
              initValue = dl.find('.' + THIS).html()
              setTimeout(function () {
                notOption(input.val(), function (none) {
                  initValue || input.val('') // none && !initValue
                }, 'blur')
              }, 200)
            })
          }

          // 选择
          dds.on('click', function () {
            var othis = $(this); var value = othis.attr('lay-value')
            var filter = select.attr('lay-filter') // 获取过滤器

            if (othis.hasClass(DISABLED)) return false

            if (othis.hasClass('layui-select-tips')) {
              input.val('')
              hiddenInput.val('')
            } else {
              input.val(othis.text())
              hiddenInput.val(value)
              othis.addClass(THIS)
            }

            othis.siblings().removeClass(THIS)
            select.val(value).removeClass('layui-form-danger')
            layui.event.call(this, MOD_NAME, 'select(' + filter + ')', {
              elem: select[0],
              value: value,
              othis: reElem
            })
            hideDown(true)
            return false
          })

          reElem.find('dl>dt').on('click', function (e) {
            return false
          })

          // 关闭下拉
          $(document).off('click', hide).on('click', hide)
        }

        selects.each(function (index, select) {
          var _that = this
          var othis = $(this)
          var hasRender = othis.next('.' + CLASS)
          var disabled = this.disabled
          var value = select.value
          var selected = $(select.options[select.selectedIndex]) // 获取当前选中项
          var selectText = value ? selected.html() : ''
          var optionsFirst = select.options[0]

          function eachSelectCreate (arr) {
        	  arr.length === 0 && arr.push('<dd lay-value="" class="' + DISABLED + '">没有选项</dd>')
 			  $('#lay-select-' + selectid).remove()
            var optionHtml = '<div id="lay-select-' + selectid + '" class="lay-select-dropdown lay-select-dropdown-unselect"><dl class="layui-anim layui-anim-upbit">' + arr.join('') + '</dl></div>'
            $('body').append(optionHtml)

            // 替代元素
            var reElem = $(['<div dropmenu="lay-select-' + selectid + '" class="' + (isSearch ? '' : 'layui-unselect ') + CLASS + (disabled ? ' layui-select-disabled' : '') + '">',
              '<input class="select-input-hidden" type="hidden" name="' + othis.attr('name') + '" value="' + value + '"/><div class="' + TITLE + '"><input type="text" placeholder="' + placeholder + '" value="' + (selectText) + '" ' + (isSearch ? '' : 'readonly') + ' class="layui-input' + (isSearch ? '' : ' layui-unselect') + (disabled ? (' ' + DISABLED) : '') + '">',
              '<i class="layui-edge"></i></div>',
              '</div>'].join(''))

            hasRender[0] && hasRender.remove() // 如果已经渲染，则Rerender
            othis.after(reElem)
            events.call(_that, reElem, disabled, isSearch)
          }

          if (typeof othis.attr('lay-ignore') === 'string') return othis.show()

          var isSearch = typeof othis.attr('lay-search') === 'string'
          var title = othis.attr('placeholder')
          var placeholder = (title != undefined) ? title : (optionsFirst ? (
            optionsFirst.value ? TIPS : (optionsFirst.innerHTML || TIPS)
          ) : TIPS)

          var selectid = that.getId()
          var url = othis.data('url')
          if (url != undefined && url != '') {
        	  $.ajax({
        		 url: url,
        		 dataType: 'json',
        		 success: function (msg) {
        			 var data = msg
        			 var arr = []
        			 layui.each(data, function (index, item) {
        				 if (item.selected != undefined && item.selected) {
	                   		  value = item.value
	                   		  selectText = item.label
	                   	 }
   	        	  		 arr.push('<dd lay-value="' + item.value + '" class="' + (value === item.value ? THIS : '') + (item.disabled ? (' ' + DISABLED) : '') + '">' + item.label + '</dd>')
       	          	 })
        			 eachSelectCreate(arr)
        		 }
        	  })
          } else {
        	  var options = othis.find('*')
            var arr = []
            layui.each(options, function (index, item) {
            	  if (item.selected != undefined && item.selected) {
            		  value = item.value
            		  selectText = item.label
            	  }
              if (index === 0 && !item.value) {
                arr.push('<dd lay-value="" class="layui-select-tips">' + (item.innerHTML || TIPS) + '</dd>')
              } else if (item.tagName.toLowerCase() === 'optgroup') {
                arr.push('<dt>' + item.label + '</dt>')
              } else {
                arr.push('<dd lay-value="' + item.value + '" class="' + (value === item.value ? THIS : '') + (item.disabled ? (' ' + DISABLED) : '') + '">' + item.innerHTML + '</dd>')
              }
            })

            var dataItems = othis.data('item')
            if (dataItems != undefined && dataItems != '') {
            	  dataItems = eval('(' + dataItems + ')')
    	          layui.each(dataItems, function (index, item) {
    	        	  if (item.selected != undefined && item.selected) {
                		  value = item.value
                		  selectText = item.label
                	  }
    	        	  arr.push('<dd lay-value="' + item.value + '" class="' + (value === item.value ? THIS : '') + (item.disabled ? (' ' + DISABLED) : '') + '">' + item.label + '</dd>')
    	          })
            }
            eachSelectCreate(arr)
          }
        })
      },
      // 复选框/开关
      checkbox: function () {
        var CLASS = {
          checkbox: ['layui-form-checkbox', 'layui-form-checked', 'checkbox'],
          _switch: ['layui-form-switch', 'layui-form-onswitch', 'switch']
        }
        var checks = elemForm.find('input[type=checkbox]')

        var events = function (reElem, RE_CLASS) {
          var check = $(this)

          // 勾选
          reElem.on('click', function () {
            var filter = check.attr('lay-filter') // 获取过滤器
            var text = (check.attr('lay-text') || '').split('|')

            if (check[0].disabled) return

            check[0].checked ? (
              check[0].checked = false
              , reElem.removeClass(RE_CLASS[1]).find('em').text(text[1])
            ) : (
              check[0].checked = true
              , reElem.addClass(RE_CLASS[1]).find('em').text(text[0])
            )

            layui.event.call(check[0], MOD_NAME, RE_CLASS[2] + '(' + filter + ')', {
              elem: check[0],
              value: check[0].value,
              othis: reElem
            })
          })
        }

        checks.each(function (index, check) {
          var othis = $(this); var skin = othis.attr('lay-skin')
          var text = (othis.attr('lay-text') || '').split('|'); var disabled = this.disabled
          if (skin === 'switch') skin = '_' + skin
          var RE_CLASS = CLASS[skin] || CLASS.checkbox

          if (typeof othis.attr('lay-ignore') === 'string') return othis.show()

          // 替代元素
          var hasRender = othis.next('.' + RE_CLASS[0])
          var reElem = $(['<div class="layui-unselect ' + RE_CLASS[0] + (
            check.checked ? (' ' + RE_CLASS[1]) : '') + (disabled ? ' layui-checkbox-disbaled ' + DISABLED : '') + '" lay-skin="' + (skin || '') + '">',
          {
            _switch: '<em>' + ((check.checked ? text[0] : text[1]) || '') + '</em><i></i>'
          }[skin] || ((check.title.replace(/\s/g, '') ? ('<span>' + check.title + '</span>') : '') + '<i class="layui-icon">' + (skin ? '&#xe605;' : '&#xe618;') + '</i>'),
          '</div>'].join(''))

          hasRender[0] && hasRender.remove() // 如果已经渲染，则Rerender
          othis.after(reElem)
          events.call(this, reElem, RE_CLASS)
        })
      },
      // 单选框
      radio: function () {
        var CLASS = 'layui-form-radio'; var ICON = ['&#xe643;', '&#xe63f;']
        var radios = elemForm.find('input[type=radio]')

        var events = function (reElem) {
          var radio = $(this); var ANIM = 'layui-anim-scaleSpring'

          reElem.on('click', function () {
            var name = radio[0].name; var forms = radio.parents(ELEM)
            var filter = radio.attr('lay-filter') // 获取过滤器
            var sameRadio = forms.find('input[name=' + name.replace(/(\.|#|\[|\])/g, '\\$1') + ']') // 找到相同name的兄弟

            if (radio[0].disabled) return

            layui.each(sameRadio, function () {
              var next = $(this).next('.' + CLASS)
              this.checked = false
              next.removeClass(CLASS + 'ed')
              next.find('.layui-icon').removeClass(ANIM).html(ICON[1])
            })

            radio[0].checked = true
            reElem.addClass(CLASS + 'ed')
            reElem.find('.layui-icon').addClass(ANIM).html(ICON[0])

            layui.event.call(radio[0], MOD_NAME, 'radio(' + filter + ')', {
              elem: radio[0],
              value: radio[0].value,
              othis: reElem
            })
          })
        }

        radios.each(function (index, radio) {
          var othis = $(this); var hasRender = othis.next('.' + CLASS); var disabled = this.disabled

          if (typeof othis.attr('lay-ignore') === 'string') return othis.show()
          hasRender[0] && hasRender.remove() // 如果已经渲染，则Rerender

          // 替代元素
          var reElem = $(['<div class="layui-unselect ' + CLASS + (radio.checked ? (' ' + CLASS + 'ed') : '') + (disabled ? ' layui-radio-disbaled ' + DISABLED : '') + '">',
            '<i class="layui-anim layui-icon">' + ICON[radio.checked ? 0 : 1] + '</i>',
            '<div>' + (function () {
              var title = radio.title || ''
              if (typeof othis.next().attr('lay-radio') === 'string') {
                title = othis.next().html()
                othis.next().remove()
              }
              return title
            }()) + '</div>',
            '</div>'].join(''))

          othis.after(reElem)
          events.call(this, reElem)
        })
      }
    }
    type ? (
      items[type] ? items[type]() : hint.error('不支持的' + type + '表单渲染')
    ) : layui.each(items, function (index, item) {
      item()
    })
    return that
  }

  // 表单提交校验
  var submit = function () {
    var button = $(this); var verify = form.config.verify; var stop = null
    var DANGER = 'layui-form-danger'; var field = {}; var elem = button.parents(ELEM)

    var verifyElem = elem.find('*[lay-verify]') // 获取需要校验的元素
    var formElem = button.parents('form')[0] // 获取当前所在的form元素，如果存在的话
    var fieldElem = elem.find('input,select,textarea') // 获取所有表单域
    var filter = button.attr('lay-filter') // 获取过滤器

    // 开始校验
    layui.each(verifyElem, function (_, item) {
      var othis = $(this)
      var vers = othis.attr('lay-verify').split('|')
      var verType = othis.attr('lay-verType') // 提示方式
      var value = othis.val()

      othis.removeClass(DANGER)
      layui.each(vers, function (_, thisVer) {
        var isTrue // 是否命中校验
        var errorText = '' // 错误提示文本
        var isFn = typeof verify[thisVer] === 'function'

        // 匹配验证规则
        if (verify[thisVer]) {
          var isTrue = isFn ? errorText = verify[thisVer](value, item) : !verify[thisVer][0].test(value)
          errorText = errorText || verify[thisVer][1]

          // 如果是必填项或者非空命中校验，则阻止提交，弹出提示
          if (isTrue) {
            // 提示层风格
            if (verType === 'tips') {
              layer.tips(errorText, (function () {
                if (typeof othis.attr('lay-ignore') !== 'string') {
                  if (item.tagName.toLowerCase() === 'select' || /^checkbox|radio$/.test(item.type)) {
                    return othis.next()
                  }
                }
                return othis
              }()), { tips: 1 })
            } else if (verType === 'alert') {
              layer.alert(errorText, { title: '提示', shadeClose: true })
            } else {
              layer.msg(errorText, { icon: 5, shift: 6 })
            }
            if (!device.android && !device.ios) item.focus() // 非移动设备自动定位焦点
            othis.addClass(DANGER)
            return stop = true
          }
        }
      })
      if (stop) return stop
    })

    if (stop) return false

    var nameIndex = {} // 数组 name 索引
    layui.each(fieldElem, function (_, item) {
      item.name = (item.name || '').replace(/^\s*|\s*&/, '')

      if (!item.name) return

      // 用于支持数组 name
      if (/^.*\[\]$/.test(item.name)) {
        var key = item.name.match(/^(.*)\[\]$/g)[0]
        nameIndex[key] = nameIndex[key] | 0
        item.name = item.name.replace(/^(.*)\[\]$/, '$1[' + (nameIndex[key]++) + ']')
      }

      if (/^checkbox|radio$/.test(item.type) && !item.checked) return
      field[item.name] = item.value
    })

    // 获取字段
    return layui.event.call(this, MOD_NAME, 'submit(' + filter + ')', {
      elem: this,
      form: formElem,
      field: field
    })
  }

  // 自动完成渲染
  var form = new Form()
  var dom = $(document); var win = $(window)

  form.render()

  // 表单reset重置渲染
  dom.on('reset', ELEM, function () {
    var filter = $(this).attr('lay-filter')
    setTimeout(function () {
      form.render(null, filter)
    }, 50)
  })

  // 表单提交事件
  dom.on('submit', ELEM, submit)
    .on('click', '*[lay-submit]', submit)

  exports(MOD_NAME, form)
})
